package mc0101.mccache.thread;

import lombok.extern.slf4j.Slf4j;
import mc0101.mccache.api.ZcRedisHandleApi;
import mc0101.mccache.cache.BaseSingletonCacheVO;
import mc0101.mccache.cache.TwoLevelLocalCache;
import mc0101.mccache.cache.ZcCacheLocalCache;
import mc0101.mccache.dto.ZcParamResponseData;
import mc0101.mccache.holder.ZcCacheApplicationContextHolder;
import mc0101.mccache.lock.ZcCacheLocalLock;
import mc0101.mccache.param.ZcCacheParam;
import mc0101.mccache.util.CacheInvalidJudgeUtils;
import mc0101.mccache.util.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
@Slf4j
public class LocalAndRemoteRefreshThread implements Runnable {


    @Autowired
    @Qualifier("zcCacheRestTemplate")
    private RestTemplate restTemplate;

    @Autowired
    private ZcCacheLocalCache zcCacheLocalCache;

    @Autowired
    private CacheInvalidJudgeUtils cacheInvalidJudgeUtils;

    @Autowired
    private ZcRedisHandleApi zcRedisHandleApi;

    @Autowired
    private TwoLevelLocalCache twoLevelLocalCache;

    @Autowired
    private ZcCacheParam zcCacheParam;

    @Value("${cache.paramServer.url}")
    private String paramServerUrl;

    @Value("${spring.application.name}")
    private String systemName;

    @Override
    public void run() {
        // 获取远程参数
        Map<String, List<Map<String, Object>>> params = getRemoteParam();

        // 更新本地缓存
        refreshLocalCache(params);

        // 更新单例缓存
        refreshSingleCache();
    }

    private Map<String, List<Map<String, Object>>> getRemoteParam() {
        long timestamp = System.currentTimeMillis();
        log.info("开始查询远程参数！");
        Map<String, Object> param = new HashMap<>();
        param.put("systemName", systemName);
        ZcParamResponseData body = restTemplate.getForEntity(paramServerUrl, ZcParamResponseData.class, param).getBody();
        if (!body.isSuccess()){
            throw new RuntimeException("调用远程获取参数失败，返回报文为：" + body);
        }

        Map<String, List<Map<String, Object>>> params = body.getData();
        if (CollectionUtils.isNullOrEmpty(params)){
            throw new RuntimeException("调用远程获取参数为空，返回报文为：" + body);
        }
        log.info("查询远程参数结束！消耗时间为：" + (System.currentTimeMillis() - timestamp) + "ms");
        return params;
    }

    private void refreshSingleCache() {
        long timestamp = System.currentTimeMillis();
        log.info("开始更新单例缓存值对象！");
        Map<String, BaseSingletonCacheVO> map = ZcCacheApplicationContextHolder.getBeanNamesForType(BaseSingletonCacheVO.class);
        if (CollectionUtils.isNullOrEmpty(map)){
            log.info("没有需要更新的自启动值对象参数缓存，更新结束！");
            return;
        }

        for (Map.Entry<String, BaseSingletonCacheVO> entry : map.entrySet()) {
            log.info("开始更新单例缓存值对象bean："+entry.getKey());
            entry.getValue().refresh();
        }
        log.info("更新单例缓存值对象结束！消耗时间为：" + (System.currentTimeMillis() - timestamp) + "ms");
    }

    private void refreshLocalCache(Map<String, List<Map<String, Object>>> params) {
        long timestamp = System.currentTimeMillis();
        log.info("开始更新本地缓存及远程版本！");
        long paramVersion = 0;
        try {
            paramVersion = refreshRemoteVersion();
        } catch (Exception ex){
            log.error("操作redis时出现异常，请检查！", ex);
            paramVersion = cacheInvalidJudgeUtils.getLocalCacheVersion();
        }

        // 更新本地缓存以及版本
        log.info("开始更新本地缓存！");
        setLocalCache(params, paramVersion);
        log.info("本地缓存更新结束！");
        log.info("更新本地缓存及远程版本结束！消耗时间为：" + (System.currentTimeMillis() - timestamp) + "ms");
    }

    private void setLocalCache(Map<String, List<Map<String, Object>>> listMap, Long paramVersion) {
        if (listMap == null || listMap.isEmpty()){
            return;
        }
        ZcCacheLocalLock.getInstance().getReadWriteLock().writeLock().lock();
        try {
            zcCacheLocalCache.refreshListMapAndClassName(listMap);
            cacheInvalidJudgeUtils.refreshVersionAndTimestamp(paramVersion);
        } finally {
            ZcCacheLocalLock.getInstance().getReadWriteLock().writeLock().unlock();
            // 本地缓存重新设置，清空二级缓存
            twoLevelLocalCache.clearCache();
        }
    }

    private long refreshRemoteVersion() throws Exception {
        String paramVersionKey = zcCacheParam.getParamVersionKey();
        String paramVersionString = zcRedisHandleApi.get(paramVersionKey);
        long paramVersion = Long.parseLong(paramVersionString != null ? paramVersionString : "0");
        log.debug("远程版本为:"+paramVersion+"，本地版本为："+cacheInvalidJudgeUtils.getLocalCacheVersion());
        if (cacheInvalidJudgeUtils.isLocalVersionOld(paramVersion)){
            log.debug("远程版本已经刷新了，为:"+paramVersion+"，就不用刷新了");
            return paramVersion;
        }

        log.debug("获取分布式锁，准备刷新远程缓存！");
        if (!zcRedisHandleApi.lockUntilPass(zcCacheParam.getDisturbuteLockKey(), zcCacheParam.getDisturbuteLockId()
                , zcCacheParam.getLockOutTime(), zcCacheParam.getTryGetLockTime())){
            throw new RuntimeException("获取分布式锁失败！");
        }

        log.debug("获取分布式锁成功，开始刷新远程缓存！");
        try {
            paramVersionString = zcRedisHandleApi.get(paramVersionKey);
            paramVersion = Long.parseLong(paramVersionString != null ? paramVersionString : "0");
            log.debug("获取锁后远程版本为:"+paramVersion+"，本地版本为："+cacheInvalidJudgeUtils.getLocalCacheVersion());
            if (cacheInvalidJudgeUtils.isLocalVersionOld(paramVersion)){
                log.debug("获取锁后发现远程版本已经刷新了，为:"+paramVersion+"，就不用刷新了");
                return paramVersion;
            }

            // 更新远程版本号
            long remoteCacheVersion = zcRedisHandleApi.incr(paramVersionKey);
            if (cacheInvalidJudgeUtils.isLocalVersionEqualsOrNew(remoteCacheVersion)){
                log.warn("远程缓存出现过瞬断，或者远程缓存丢失的情况，"+"远程版本为:"+paramVersion+"，本地版本为："+cacheInvalidJudgeUtils
                        .getLocalCacheVersion()+"，这个时候版本号+10000");
                log.warn("远程版本为:"+paramVersion+"，本地版本为："+cacheInvalidJudgeUtils.getLocalCacheVersion());
                long resultVersion = cacheInvalidJudgeUtils.getLocalCacheVersion() + 10000;
                zcRedisHandleApi.set(paramVersionKey, String.valueOf(resultVersion));
                return resultVersion;
            }
            return remoteCacheVersion;
        } finally {
            zcRedisHandleApi.releaseLock(zcCacheParam.getDisturbuteLockKey(), zcCacheParam.getDisturbuteLockId());
        }

    }
}